/**
  ETFRoot project, Anyang Normal University && IMP-CAS
  \class ETFGlobalTracker
  \brief for tracking around the reaction target with united tracking algorithm,
  making use of postMag track info and preTaTrk-postTaTrk inconsistencies
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-11-10
  \date 2021-11-25 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-11-10   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <TClonesArray.h>
#include "FairRootManager.h"
#include "ETFGlobalTracker.h"
#include "ETFDetectorManager.h"
#include "ETFPDCL.h"
#include "ETFPDCR.h"
#include "ETFDCHit.h"
#include "ETFMath.h"
#include "ETFMsg.h"
#include "ETFTrackTa.h"
#include "ETFTrackTa4.h"
#include "ETFTimer.h"
#include "ETFTaTracker.h"

ClassImp(ETFGlobalTracker);

// dsqaure threshold per dot - usually set to be the same as ETFTrackTa's
double ETFGlobalTracker::D2THREPD = 40.;

ETFGlobalTracker::ETFGlobalTracker(const char *name) : ETFTaTracker(name){
  if(!strcmp(name, "")) return;

  ETFDetectorManager *m = ETFDetectorManager::Instance();
  fDCs.push_back(dynamic_cast<ETFVDC *>(m->GetDetector("pdcU0")));
  fDCs.push_back(dynamic_cast<ETFVDC *>(m->GetDetector("pdcU1")));
  fDCs.push_back(dynamic_cast<ETFVDC *>(m->GetDetector("pdcD0")));
  fDCs.push_back(dynamic_cast<ETFVDC *>(m->GetDetector("pdcD1")));
  fTimers[0] = dynamic_cast<ETFTimer *>(m->GetDetector("tstop0")); // for DCTaArrU
  fTimers[1] = dynamic_cast<ETFTimer *>(m->GetDetector("tstop1")); // for DCTaArrD

  for(auto &p : fTracks) if(p){ delete p; p = nullptr; }
  for(auto &p : fTracksOut) if(p){ delete p; p = nullptr; }
  if(f3DTracks){ delete f3DTracks; f3DTracks = nullptr; } // currently not used
  fTracks[0] = new TClonesArray("ETFTrackTa4");
  fTracksOut[0] = new TClonesArray("ETFTrackTa4");
  fTracksOut[0]->SetName(name);

  // TODO: user input configuration expected here
  D2THREPD = 40.;
} // end ctor

ETFGlobalTracker::~ETFGlobalTracker(){}

// X tracking
void ETFGlobalTracker::Map(){
  // not eligible for tracking
  if(4 != fTimers[0]->GetFiredStatus() || 4 != fTimers[1]->GetFiredStatus()) return;
  Map(TrackType::kX);
  UpdateHitPos();
} // end member functuion Map

// 3D tracking
void ETFGlobalTracker::Map3D(){
  ETFMsg::Warn("ETFGlobalTracker", "Map3D: not to be used for global tracker.");
} // end member functuion Map3D

bool ETFGlobalTracker::SetDriftDistance(ETFVTrack *preTaTrk, ETFVTrack *postTaTrk){
  return fDCs[0]->SetDriftDistance(preTaTrk) && fDCs[fDCs.size()-1]->SetDriftDistance(postTaTrk);
} // end member function SetDriftDistance
bool ETFGlobalTracker::SetDriftDistance(ETFVTrack *trk){
  ETFTrackTa4 *t = dynamic_cast<ETFTrackTa4 *>(trk);
  if(!t) ETFMsg::Error("ETFGlobalTracker", "SetDriftDistance: trk is not of ETFTrackTa4");
  return SetDriftDistance(t->GetPreTaTrack(), t->GetPostTaTrack());
} // end if


void ETFGlobalTracker::SetPostMagTrk(ETFVTrack *trk){
  SetPostMagTrk(trk->GetK(), trk->GetB());
} // end member function SetPostMagTrk

// called by Map, to test if a hit combination is eligible
bool ETFGlobalTracker::TestCombination(TrackType type){
  if(TrackType::kX != type){
    ETFMsg::Error("ETFGlobalTracker", "TestCombination: only X tracks are allowed here");
    return false;
  } // end if

  const short t = short(type);
  // fitting without drift distance info //
  ETFTrackTa4 *trk4 = dynamic_cast<ETFTrackTa4 *>(ConstructedAt(fTracks[0]));
  ETFTrackTa *trk[2] = {trk4->GetPreTaTrack(), trk4->GetPostTaTrack()};
  // assign the drift time start
  trk4->Initialize(); trk4->SetId(fTracks[t]->GetEntries()-1, t);
  if(-9999. == fK3 || -9999. == fB3)
    ETFMsg::Error("ETFGlobalTracker", "TestCombination: fK3 or fB3 not set");
  trk4->SetPostMagTrk(fK3, fB3);
  for(auto &p : fHits) if(p.second) trk[p.second->GetLayerId()/4]->AddHit(p.second);
  if(!trk4->EnoughHit()){ fTracks[t]->Remove(trk4); return false; }
  // check d2: fitting quality without drift distance info
  if(trk[0]->GetD2() > D2THREPD*trk[0]->GetNofHits() ||
     trk[1]->GetD2() > D2THREPD*trk[1]->GetNofHits() ){
    fTracks[t]->Remove(trk4); return false;
  } // end if

  // fitting with drift distance info //
  for(int i = 2; i--;) trk[i]->SetDriftTimeStart(fTimers[i]->GetTime());
  // here whether drift time is within range is checked
  if(!SetDriftDistance(trk4)){ fTracks[0]->Remove(trk4); return false; }
  // fit the track with drift distance, and assign the fitting residues
  // using space-time relation specific to post-mag dc, i.e. ETFDC
  // if the fitting result fails the user requirements, returns false
  if(fChiPD > 0.) trk4->SetChiPD(fChiPD); // user-input chiPD
  if(!trk4->Fit()){ fTracks[0]->Remove(trk4); return false; }
  // compare the current track with the existing tracks for compatibility check
  // remove the track if incompatibility is encountered
  if(!Compare(trk4, TrackType::kX)){ fTracks[0]->Remove(trk4); return false; }
  return true; // new trk passed all the test - We can finally accept them
} // end member function TestCombination

// replace X tracks in pre/post-TaTrackers with ameliorated global ones
// NOTE that this method is a little tricky: since the fTracksOut-s are
// left alone, and only fTracks are updated to ameliorated tracks, both
// for fPreTaTracker and fPostTaTracker, we can access the ameliorated
// tracks around TA either through ETFGlobalTracker or ETFPreTaTracker
// and ETFPostTaTracker in code. But the original un-ameliorated trks
// around TA are persistently sunk into the ROOT file, so that the trks
// in the ROOT file are not duplicated.
void ETFGlobalTracker::UpdateTracks(ETFTaTracker *pre, ETFTaTracker *post){
  // clear X tracks in pre and post
  pre->GetTracks()->Clear();
  post->GetTracks()->Clear();
  // fill in new ones
  int i = 0;
  for(auto p : *fTracks[0]){
    ETFTrackTa *t0 = dynamic_cast<ETFTrackTa *>(pre->GetTracks()->ConstructedAt(i));
    ETFTrackTa *t1 = dynamic_cast<ETFTrackTa *>(post->GetTracks()->ConstructedAt(i));
    ETFTrackTa4 *t = dynamic_cast<ETFTrackTa4 *>(p);
    *t0 = *t->GetPreTaTrack(); *t1 = *t->GetPostTaTrack();
    i++;
  } // end for over p
  pre->TrackMerge(); post->TrackMerge();
} // end member function UpdateTracks

void ETFGlobalTracker::Initialize(){
  ETFTaTracker::Initialize();
  fK3 = -9999.; fB3 = -9999.;
} // end member function Initialize
